//
// Created by QU on 24-6-18.
//
#include <iostream>
#include <fstream>
#include <sstream>
#include <functional>
#include <utility>
#include <memory>
#include <vector>
#include <map>
#include <set>

using std::cout, std::cin, std::endl;

struct X {
    int value = 1;
    void func(int val) const{
        std::cout << this->value << std::endl;
    }
};

void f2(void (X::*func)(int val), X* x){
    // 这里同样是符号优先级的原因, 必须加括号.
    // x->*func 还是 x.*func  取决于x是一个引用还是一个指针.
    (x->*func)(x->value);
}

int main(int argc, char const *argv[]) {
//    成员指针 分为两种: 数据成员指针, 成员函数指针
//
//    1.成员指针分为成员函数指针，数据成员指针，注意，数据成员指针或虚函数成员指针并没有真的指向一个内存，它只是表示表示在当前的类，那个字段的位置而已，
//        比如&Xvalue表示的只是这个数据成员value在X类中的位置。
//    2.数据成员指针和虚成员函数指针是一个似偏移量的东西，成员函数指针则真的存储了一个地址。(我们将在最后为了讲述这个）
//    3.成员指针也没办法脱离类的实例对象单独使用，不管是非静态数据成员指针还是非静态成员函数指针（非要强制转换成员函数指针也是可以的）。
//    4.静态数据成员和静态成员函数不与类关联，也就不参与这个成员指针的讨论了，切记。
//        实际上我之前打算是直接说成员指针不是指针，毕竟std:is_pointer也没把它包含在内，也不同于一般的指针的行为，但是，
//        标准没规定它是什么，所以我也就严谨一点，只说，成员指针不是常规的指针。

    X x;
    // 数据成员指针
    int X::*data_member = &X::value;            // 一个名字是data_member的数据成员指针, 指向了类X的value数据成员.
    int &val = (x.*data_member);                // 一个实例, 通过这个指针, 访问这个实例的数据成员指向的位置的(也就是value)成员.
    // 成员函数指针, 必须加括号(因为符号优先级的原因)
    void (X::*func)(int) const = &X::func;
    // 上面的你可以使用typedef, using定义一个别名或者auto.....

    // 实际中, 我们不太可能写这种东西, 要么使用模版, 要么使用标准库封装好的包装函数
    std::invoke(&X::func, x, 10);
    // 后续在看吧, c++17 有的std::invoke...


// 成员值存储了什么
// 严格来说, 存什么东西标准中没有说, 完全靠编译器实现决定.
//
// 如果是普通成员函数指针, 存储的是函数的地址, 如果没有这个地址我们不可能jmp过去.
// 如果是虚成员函数指针, 存储的是偏移量.
// 如果是数据成员指针, 存储的是偏移量.

    return 0;
}